package main

import (
	"database/sql"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"strconv"

	"test.com/fusion"
	"test.com/worlddb"
)

type QuestPrototype struct {
	QuestName      string                  `json:"questName"`
	QuestDesc      string                  `json:"questDesc"`
	QuestIntro     string                  `json:"questIntro"`
	QuestDone      string                  `json:"questDone"`
	QuestNotDone   string                  `json:"questNotDone"`
	QuestCustomize string                  `json:"questCustomize"`
	QuestInfo      *worlddb.QuestPrototype `json:"questInfo"`
}

type questWebPrototype struct {
	QuestPrototype
}

func handleGetQuestTemplates(w http.ResponseWriter, r *http.Request) {
	allTemplates := map[string]interface{}{
		"QuestChequeReq": fusion.MarshalEntityToInterface(
			new(worlddb.QuestChequeReq), "json"),
		"QuestItemReq": fusion.MarshalEntityToInterface(
			new(worlddb.QuestItemReq), "json"),
		"QuestChequeInit": fusion.MarshalEntityToInterface(
			new(worlddb.QuestChequeInit), "json"),
		"QuestItemInit": fusion.MarshalEntityToInterface(
			new(worlddb.QuestItemInit), "json"),
		"QuestChequeReward": fusion.MarshalEntityToInterface(
			new(worlddb.QuestChequeReward), "json"),
		"QuestItemReward": fusion.MarshalEntityToInterface(
			new(worlddb.QuestItemReward), "json"),
		"QuestScript": fusion.MarshalEntityToInterface(
			new(worlddb.QuestScript), "json"),
		"QuestCondition": fusion.MarshalEntityToInterface(
			new(worlddb.QuestCondition), "json"),
	}
	jsonData, err := json.Marshal(allTemplates)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeJsonResponseData(w, jsonData)
}

func handleGetQuestDialogArgs(w http.ResponseWriter, r *http.Request) {
	scriptFiles, err := getAllScriptable(worldDB)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	mapNames, err := getAllMapNames(worldDB)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	lmRows, err := getAllLandmark4DialogTable(worldDB)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	charNames, err := getAllCharNames(worldDB)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	charSpawnRows, err := getAllCharSpawn4DialogTable(worldDB)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	charProtoRows, err := getAllCharProto4DialogTable(worldDB)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	sobjNames, err := getAllSObjNames(worldDB)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	sobjSpawnRows, err := getAllSObjSpawn4DialogTable(worldDB)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	sobjProtoRows, err := getAllSObjProto4DialogTable(worldDB)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	spellRows, err := getAllSpell4DialogTable(worldDB)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	questRows, err := getAllQuest4DialogTable(worldDB)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	itemRows, err := getAllItem4DialogTable(worldDB)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	questionRows, err := getAllQuestion4DialogTable(worldDB)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	dialogArgs := map[string]interface{}{
		"scriptFiles":   scriptFiles,
		"mapNames":      mapNames,
		"lmRows":        lmRows,
		"charNames":     charNames,
		"charSpawnRows": charSpawnRows,
		"charProtoRows": charProtoRows,
		"sobjNames":     sobjNames,
		"sobjSpawnRows": sobjSpawnRows,
		"sobjProtoRows": sobjProtoRows,
		"spellRows":     spellRows,
		"questRows":     questRows,
		"itemRows":      itemRows,
		"questionRows":  questionRows,
	}
	jsonData, err := json.Marshal(dialogArgs)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeJsonResponseData(w, jsonData)
}

func handleGetQuestEnumType(w http.ResponseWriter, r *http.Request) {
	enumTypes := map[string]interface{}{
		"ScriptType":         getEnumScriptType(),
		"QuestObjType":       getEnumQuestObjType(),
		"QuestWhenType":      getEnumQuestWhenType(),
		"QuestConditionType": getEnumQuestConditionType(),
	}
	jsonData, err := json.Marshal(enumTypes)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeJsonResponseData(w, jsonData)
}

func handleGetQuestUIView(w http.ResponseWriter, r *http.Request) {
	uiViews := map[string]interface{}{
		"QuestClassType":     getViewQuestClassType(),
		"QuestObjType":       getViewQuestObjType(),
		"QuestRepeatType":    getViewQuestRepeatType(),
		"QuestConditionType": getViewQuestConditionType(),
		"MapType":            getViewMapType(),
		"PlayerCareer":       getViewPlayerCareer(),
		"PlayerGender":       getViewPlayerGender(),
		"ChequeType":         getViewChequeType(),
		"ItemFlowType":       getViewItemFlowType(),
	}
	jsonData, err := json.Marshal(uiViews)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeJsonResponseData(w, jsonData)
}

func handleGetQuestTreeView(w http.ResponseWriter, r *http.Request) {
	handleGetEditorTreeView(
		w, r, worlddb.EditorTreeView_Type_Quest, getAllQuestNames)
}

func handleSaveQuestTreeView(w http.ResponseWriter, r *http.Request) {
	handleSaveEditorTreeView(w, r, worlddb.EditorTreeView_Type_Quest)
}

func handleGetQuestInstance(w http.ResponseWriter, r *http.Request) {
	questID, err := strconv.ParseUint(r.FormValue("QuestID"), 0, 32)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	questData, err := getQuestInstanceJsonData(worldDB, uint32(questID))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeJsonResponseData(w, questData)
}

func handleNewQuestInstance(w http.ResponseWriter, r *http.Request) {
	questName, questDesc := r.FormValue("QuestName"), r.FormValue("QuestDesc")
	if questName == "" {
		http.Error(w, "empty quest name.", http.StatusBadRequest)
		return
	}
	var questID uint32
	err := fusion.RunDBTransaction(worldDB, func(tx *sql.Tx) (err error) {
		questID, err = newQuestInstance(tx, questName, questDesc)
		return
	})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	questData, err := getQuestInstanceJsonData(worldDB, questID)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeJsonResponseData(w, questData)
}

func handleCopyQuestInstance(w http.ResponseWriter, r *http.Request) {
	questID, err := strconv.ParseUint(r.FormValue("QuestID"), 0, 32)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	var ID uint32
	err = fusion.RunDBTransaction(worldDB, func(tx *sql.Tx) (err error) {
		ID, err = copyQuestInstance(worldDB, tx, uint32(questID))
		return
	})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	questData, err := getQuestInstanceJsonData(worldDB, ID)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeJsonResponseData(w, questData)
}

func handleDeleteQuestInstance(w http.ResponseWriter, r *http.Request) {
	questID, err := strconv.ParseUint(r.FormValue("QuestID"), 0, 32)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	err = fusion.RunDBTransaction(worldDB, func(tx *sql.Tx) error {
		return deleteQuestInstance(tx, uint32(questID))
	})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeOKResponseData(w)
}

func handleSaveQuestInstance(w http.ResponseWriter, r *http.Request) {
	questData, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	var questProto QuestPrototype
	if err = json.Unmarshal(questData, &questProto); err != nil {
		http.Error(w, err.Error(), http.StatusNotAcceptable)
		return
	}
	err = fusion.RunDBTransaction(worldDB, func(tx *sql.Tx) error {
		return saveQuestInstance(tx, &questProto)
	})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeOKResponseData(w)
}
